#include <stdarg.h>
#include <stdio.h>
#include <sys/time.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/un.h>
#include <fcntl.h>
#include <string>
#include "hicore.h"
#include "hifastlog.h"

HiLogInterface* _hi_log = new HiFastLog();
HiFastLog::HiFastLog()
{
    _level = HiLogLevel::Trace;
    log_data = new char[LOG_MAX_SIZE];

    fd = -1;
    log_to_file_tank = false;
}

HiFastLog::~HiFastLog()
{
    hi_deletep(log_data);

    if (fd > 0) {
        ::close(fd);
        fd = -1;
    }
}

int HiFastLog::initialize()
{
    int ret = 0;
    return ret;
}

void HiFastLog::log_set(const char *log_filename)
{
    filename += log_filename;
    printf("the logfile name is %s \n",filename.c_str());

}

void HiFastLog::verbose(const char *tag, int context_id, const char *fmt, ...)
{
    if (_level > HiLogLevel::Verbose) {
        return;
    }

    int size = 0;
    if (!generate_header(false, tag, context_id, "verb", &size)) {
        return;
    }

    va_list ap;
    va_start(ap, fmt);
    size += vsnprintf(log_data + size, LOG_MAX_SIZE - size, fmt, ap);
    va_end(ap);

    write_log(fd, log_data, size, HiLogLevel::Verbose);
    memset(log_data,'\0',sizeof(log_data));
}


void HiFastLog::info(const char *tag, int context_id, const char *fmt, ...)
{
    if (_level > HiLogLevel::Info) {
        return;
    }

    int size = 0;
    if (!generate_header(false, tag, context_id, "info", &size)) {
        return;
    }

    va_list ap;
    va_start(ap, fmt);
    size += vsnprintf(log_data + size, LOG_MAX_SIZE - size, fmt, ap);
    va_end(ap);

    write_log(fd, log_data, size, HiLogLevel::Info);
}

void HiFastLog::trace(const char *tag, int context_id, const char *fmt, ...)
{
    if (_level > HiLogLevel::Trace) {
        return;
    }

    int size = 0;
    if (!generate_header(false, tag, context_id, "trace", &size)) {
        return;
    }

    va_list ap;
    va_start(ap, fmt);
    size += vsnprintf(log_data + size, LOG_MAX_SIZE - size, fmt, ap);
    va_end(ap);

    write_log(fd, log_data, size, HiLogLevel::Info);
}

void HiFastLog::warn(const char *tag, int context_id, const char *fmt, ...)
{
    if (_level > HiLogLevel::Warn) {
        return;
    }

    int size = 0;
    if (!generate_header(false, tag, context_id, "warn", &size)) {
        return;
    }

    va_list ap;
    va_start(ap, fmt);
    size += vsnprintf(log_data + size, LOG_MAX_SIZE - size, fmt, ap);
    va_end(ap);

    write_log(fd, log_data, size, HiLogLevel::Warn);
}

void HiFastLog::error(const char *tag, int context_id, const char *fmt, ...)
{
    if (_level > HiLogLevel::Error) {
        return;
    }

    int size = 0;
    if (!generate_header(false, tag, context_id, "error", &size)) {
        return;
    }

    va_list ap;
    va_start(ap, fmt);
    size += vsnprintf(log_data + size, LOG_MAX_SIZE - size, fmt, ap);
    va_end(ap);

    write_log(fd, log_data, size, HiLogLevel::Error);
}

bool HiFastLog::generate_header(bool error, const char *tag, int context_id, const char *level_name, int *header_size)
{
    // clock time
    timeval tv;
    if (gettimeofday(&tv, NULL) == -1) {
        return false;
    }

    // to calendar time
    struct tm* tm;
    if ((tm = localtime(&tv.tv_sec)) == NULL) {
        return false;
    }

    // write log header
    int log_header_size = -1;

    if (error) {
        if (tag) {
            log_header_size = snprintf(log_data, LOG_MAX_SIZE,
                                       "[%04d-%02d-%02d][%02d:%02d:%02d.%03d][%s][%s][%d][%d] ",
                                       tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
                                       tm->tm_hour, tm->tm_min, tm->tm_sec, (int)(tv.tv_usec / 1000),
                                       level_name, tag,context_id, errno);
        } else {
            log_header_size = snprintf(log_data, LOG_MAX_SIZE,
                                       "[%04d-%02d-%02d][%02d:%02d:%02d.%03d][%s][%d][%d] ",
                                       tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
                                       tm->tm_hour, tm->tm_min, tm->tm_sec, (int)(tv.tv_usec / 1000),
                                       level_name,context_id, errno);
        }
    } else {
        if (tag) {
            log_header_size = snprintf(log_data, LOG_MAX_SIZE,
                                       "[%04d-%02d-%02d][%02d:%02d:%02d.%03d][%s][%s][%d] ",
                                       tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
                                       tm->tm_hour, tm->tm_min, tm->tm_sec, (int)(tv.tv_usec / 1000),
                                       level_name, tag,context_id);
        } else {
            log_header_size = snprintf(log_data, LOG_MAX_SIZE,
                                       "[%04d-%02d-%02d][%02d:%02d:%02d.%03d][%s][%d] ",
                                       tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
                                       tm->tm_hour, tm->tm_min, tm->tm_sec, (int)(tv.tv_usec / 1000),
                                       level_name,context_id);
        }
    }

    if (log_header_size == -1) {
        return false;
    }

    // write the header size.
    *header_size = hi_min(LOG_MAX_SIZE - 1, log_header_size);

    return true;
}

void HiFastLog::write_log(int &fd, char *str_log, int size, int level)
{
#if 0
    int rz = 0;
    int sock;
    socklen_t addr_len;
    struct sockaddr_un addr;
    struct timeval tv;

    // ensure the tail and EOF of string
    //      LOG_TAIL_SIZE for the TAIL char.
    //      1 for the last char(0).
    size = hi_min(LOG_MAX_SIZE - 1 - LOG_TAIL_SIZE, size);

    // add some to the end of char.
    str_log[size++] = LOG_TAIL;

    // if not to file, to console and return.
    if (!log_to_file_tank) {
        // if is error msg, then print color msg.
        // \033[31m : red text code in shell
        // \033[32m : green text code in shell
        // \033[33m : yellow text code in shell
        // \033[0m : normal text code
        if (level <= HiLogLevel::Trace) {
            printf("%.*s", size, str_log);
        } else if (level == HiLogLevel::Warn) {
            printf("\033[33m%.*s\033[0m", size, str_log);
        } else{
            printf("\033[31m%.*s\033[0m", size, str_log);
        }
    }

    write_log_file(str_log,size);

    sock = socket(AF_UNIX, SOCK_DGRAM, 0);
    if (sock < 0)
    {
        perror("opening stream socket");
        return;
    }

    tv.tv_sec = 3;
    tv.tv_usec = 0;
    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strcpy(addr.sun_path, LOG_SOCK_PATH);
    addr_len = sizeof(addr);
    setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char*)&tv, sizeof(tv));

    if(connect(sock, (struct sockaddr *) &addr, addr_len) < 0)
    {
        //printf("connect error logserver \n");
        close(sock);
        return;
    }

    rz = write(sock, str_log, size);
    if (rz < 0)
    {
        printf("write error \n");
        close(sock);
        return;
    }
    close(sock);
#else
    size = hi_min(LOG_MAX_SIZE - 1 - LOG_TAIL_SIZE, size);

    // add some to the end of char.
    str_log[size++] = LOG_TAIL;

    // if not to file, to console and return.
    if (!log_to_file_tank) {
        // if is error msg, then print color msg.
        // \033[31m : red text code in shell
        // \033[32m : green text code in shell
        // \033[33m : yellow text code in shell
        // \033[0m : normal text code
        if (level <= HiLogLevel::Trace) {
            printf("%.*s", size, str_log);
        } else if (level == HiLogLevel::Warn) {
            printf("\033[33m%.*s\033[0m", size, str_log);
        } else{
            printf("\033[31m%.*s\033[0m", size, str_log);
        }

        return;
    }
#endif
}

void HiFastLog::write_log_file(char *str_log,int size)
{
#if 0
    FILE *rz = 0;
    if (filename.empty()) {
        //printf("file name is empty\n");
        return;
    }


    fd = fopen(filename.c_str(),"a+");

    if(fd == -1 && errno == ENOENT) {
        printf("fopen fail %s\n",filename.c_str());
        return;
    }

    rz = fwrite(str_log, size, 1,fd);
    if(rz < 0)
    {
        printf("fwrite error\n");
    }
    fflush(fd);
    fclose(fd);
#endif

}
